#include "codeeditor.h"
#include "QPainter"
#include <QMouseEvent>
#include <QDesktopServices>
#include <QTextBlock>
#include <QTextCursor>
#include <QTextDocument>
#include <QRegularExpression>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>

CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
    // 初始化正则表达式，用于检测 URL 和邮件地址
    urlRegex.setPattern(R"(\b(?:https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]|mailto:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,})");
    urlRegex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);


    lineNumberArea = new LineNumberArea(this);

    connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
    connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea);
    connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
    connect(this,&CodeEditor::textChanged,this,&CodeEditor::onTextChanged);

    updateLineNumberAreaWidth(0);
    highlightCurrentLine();
}

// 保存书签
void CodeEditor::saveBookmarks(const QString &filePath)
{
    QFileInfo fileInfo(filePath);
    QString bookmarkFilePath = fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".bookmarks";

    QJsonArray bookmarksArray;
    for (auto it = bookmarks.constBegin(); it != bookmarks.constEnd(); ++it) {
        QJsonObject bookmarkObject;
        bookmarkObject["lineNumber"] = it.key();  // 获取键（行号）
        bookmarkObject["description"] = it.value();  // 获取值（描述）
        bookmarksArray.append(bookmarkObject);
    }

    QJsonObject rootObject;
    rootObject["bookmarks"] = bookmarksArray;

    QJsonDocument document(rootObject);
    QByteArray data = document.toJson();

    QFile file(bookmarkFilePath);
    if (file.open(QIODevice::WriteOnly)) {
        file.write(data);
        file.close();
    }
}

// 加载书签
// void CodeEditor::loadBookmarks(const QString &filePath)
// {
//     QFileInfo fileInfo(filePath);
//     QString bookmarkFilePath = fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".bookmarks";

//     QFile file(bookmarkFilePath);
//     if (file.open(QIODevice::ReadOnly)) {
//         QByteArray data = file.readAll();
//         file.close();

//         QJsonDocument document(QJsonDocument::fromJson(data));
//         QJsonObject rootObject = document.object();
//         QJsonArray bookmarksArray = rootObject["bookmarks"].toArray();

//         bookmarks.clear();
//         for (const QJsonValue &value : bookmarksArray) {
//             QJsonObject bookmarkObject = value.toObject();
//             int lineNumber = bookmarkObject["lineNumber"].toInt();
//             QString description = bookmarkObject["description"].toString();
//             bookmarks[lineNumber] = description;
//         }
//     }
// }
void CodeEditor::loadBookmarks(const QString &filePath)
{
    QFileInfo fileInfo(filePath);
    QString bookmarkFilePath = fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".bookmarks";

    QFile file(bookmarkFilePath);
    if (file.open(QIODevice::ReadOnly)) {
        QByteArray data = file.readAll();
        file.close();

        QJsonDocument document(QJsonDocument::fromJson(data));
        if (document.isObject()) {
            QJsonObject rootObject = document.object();
            if (rootObject.contains("bookmarks") && rootObject["bookmarks"].isArray()) {
                QJsonArray bookmarksArray = rootObject["bookmarks"].toArray();
                bookmarks.clear();
                for (const QJsonValue &value : bookmarksArray) {
                    if (value.isObject()) {
                        QJsonObject bookmarkObject = value.toObject();
                        int lineNumber = bookmarkObject["lineNumber"].toInt();
                        QString description = bookmarkObject["description"].toString();
                        bookmarks[lineNumber] = description;
                    }
                }
            }
        }
    }
}

void CodeEditor::addBookmark(const QString &name, int lineNumber)
{
    bookmarks[lineNumber] = name;
    saveBookmarks(filePath); // 保存书签
}

void CodeEditor::removeBookmark(int lineNumber)
{
    bookmarks.remove(lineNumber);
    saveBookmarks(filePath); // 保存书签
}

void CodeEditor::jumpToBookmark(int lineNumber)
{
    QTextCursor cursor = textCursor();
    cursor.movePosition(QTextCursor::Start);
    cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, lineNumber);
    setTextCursor(cursor);
}

QMap<int, QString> CodeEditor::getBookmarks() const
{
    return bookmarks;
}

void CodeEditor::mousePressEvent(QMouseEvent *event)
{
    QPlainTextEdit::mousePressEvent(event);
    cursor = textCursor();
    cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
}

void CodeEditor::mouseReleaseEvent(QMouseEvent *event)
{
    QPlainTextEdit::mouseReleaseEvent(event);
    QTextCursor cursor = textCursor();
    cursor.setPosition(cursor.position(), QTextCursor::KeepAnchor);

    QString selectedText = cursor.selectedText();
    QRegularExpressionMatch match = urlRegex.match(selectedText);
    if (match.hasMatch()) {
        clickedUrl = QUrl(match.captured());
        QDesktopServices::openUrl(clickedUrl);
    }
}

void CodeEditor::setTextChanged(bool value)
{
    textIsChanged = value;
}

void CodeEditor::setFilePath(const QString &fileName)
{
    filePath = fileName;
}

int CodeEditor::lineNumberAreaWidth()
{
    int digits = 1;
    int max = qMax(1, blockCount());
    while (max >= 10) {
        max /= 10;
        ++digits;
    }

    int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;

    return space;
}

void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
{
    setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}

void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
{
    if (dy)
        lineNumberArea->scroll(0, dy);
    else
        lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());

    if (rect.contains(viewport()->rect()))
        updateLineNumberAreaWidth(0);
}

void CodeEditor::hideLineNumberArea(bool flag)
{
    if(flag){
        lineNumberArea->hide();
        setViewportMargins(0, 0, 0, 0);
    } else {
        lineNumberArea->show();
        setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
    }
}

void CodeEditor::resizeEvent(QResizeEvent *e)
{
    QPlainTextEdit::resizeEvent(e);

    QRect cr = contentsRect();
    lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}

void CodeEditor::highlightCurrentLine()
{
    QList<QTextEdit::ExtraSelection> extraSelections;

    if (!isReadOnly()) {
        QTextEdit::ExtraSelection selection;

        QColor lineColor = QColor(Qt::yellow).lighter(160);

        selection.format.setBackground(lineColor);
        selection.format.setProperty(QTextFormat::FullWidthSelection, true);
        selection.cursor = textCursor();
        selection.cursor.clearSelection();
        extraSelections.append(selection);
    }

    setExtraSelections(extraSelections);
}

void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
{
    QPainter painter(lineNumberArea);
    painter.fillRect(event->rect(), Qt::red);
    QTextBlock block = firstVisibleBlock();
    int blockNumber = block.blockNumber();
    int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top());
    int bottom = top + qRound(blockBoundingRect(block).height());
    while (block.isValid() && top <= event->rect().bottom()) {
        if (block.isVisible() && bottom >= event->rect().top()) {
            QString number = QString::number(blockNumber + 1);
            painter.setPen(Qt::black);
            painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
                             Qt::AlignRight, number);
        }

        block = block.next();
        top = bottom;
        bottom = top + qRound(blockBoundingRect(block).height());
        ++blockNumber;
    }
}

//重写 textChanged() 方法
void CodeEditor::onTextChanged()
{
    // QPlainTextEdit::textChanged(); // 调用基类的 textChanged() 方法
    emit textChanged1(); // 发出信号
}
